Spring Cloud | Note-2

Spring Cloud微服务 | Note(2)

@2018年8月2日 09:40:55

环境:

JDK8+,Graedle4+,Spring Boot 2.0+,Apache HttpClient

数据来源:

中华万年历API(注意乱码处理)

http://wthrcdn.etouch.cn/weather_mini?city=深圳

http://wthrcdn.etouch.cn/weather_mini?citykey=101280601

应用@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Data
public class WeatherResponse implements Serializable{
private Weather data;
private Integer status;
private String desc;
}
@Data
public class Weather implements Serializable{
// 城市
private String city;
// ...属性根据JSON返回数据编写
}
// 天气数据接口
public interface WeatherDataService {
// 根据城市ID查询天气数据
WeatherResponse getDataByCityId(String cityId);
// 根据城市名称查询天气数据
WeatherResponse getDataByCityName(String cityName);
}
// 实现
@Service
public class WeatherDataServiceImpl implements WeatherDataService {
private static final String WEATHER_URL = "http://wthrcdn.etouch.cn/weather_mini?";
@Autowired
private RestTemplate restTemplate;
@Override
public WeatherResponse getDataByCityId(String cityId) {
String url = WEATHER_URL + "citykey=" + cityId;
return this.doGetWeather(url);
}
private WeatherResponse doGetWeather(String url) {
ResponseEntity<String> respString = restTemplate.getForEntity(url, String.class);
// JSON数据转化为对应类
ObjectMapper mapper = new ObjectMapper();
WeatherResponse resp = null;
String strBody = null;
if (respString.getStatusCodeValue() == 200) {
strBody = respString.getBody();
}
try {
resp = mapper.readValue(strBody, WeatherResponse.class);
} catch (IOException e) {
e.printStackTrace();
}
return resp;
}
}
// config(见附录)
@Configuration
public class RestConfiguration {
@Autowired
private RestTemplateBuilder builder;
@Bean
public RestTemplate restTemplate(){
return builder.build();
}
}

使用Redis提升并发访问能力

Q1:由于依赖的第三方API数据,强依赖会导致长延时(请求-请求-返回-解析-返回),来回两次HTTP请求
Q2:免费接口的风险,限制访问次数
Q3:对第三方的并发冲击
解决办法:Redis

及时响应;减少服务调用;

应用@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private final static Logger logger = LoggerFactory.getLogger(WeatherDataServiceImpl.class);
private static final long TIME_OUT = 1800L;
@Autowired
private StringRedisTemplate stringRedisTemplate;

// 修改WeaterDataServiceImpl中的doGetWeather()
private WeatherResponse doGetWeather(String url) {
String key = url;
String strBody = null;
// JSON数据转化为对应类
ObjectMapper mapper = new ObjectMapper();
WeatherResponse resp = null;
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
// 先查询Redis缓存
if (stringRedisTemplate.hasKey(key)) {
logger.info("REDIS HAS DATA");
strBody = ops.get(key);
} else {
logger.info("REDIS DOESN'T HAS DATA");
// 缓存不存在,调用接口
ResponseEntity<String> respString = restTemplate.getForEntity(url, String.class);
if (respString.getStatusCodeValue() == 200) {
strBody = respString.getBody();
}
// 数据写入缓存
ops.set(url, strBody, TIME_OUT, TimeUnit.SECONDS);
}
// 类型返回
try {
resp = mapper.readValue(strBody, WeatherResponse.class);
} catch (IOException e) {
logger.error("ERROR:" + e.toString());
}
return resp;
}

实现数据的同步

Quartz Scheduler定时获取数据
应用@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Configuration
public class QuartzConfiguration {
// JobDetail
@Bean
public JobDetail weatherDataSyncJobDetail(){
return JobBuilder.newJob(WeatherDataSyncJob.class)
.withDescription("WeatherDataSyncJob").storeDurably().build();
}
// Trigger
@Bean
public Trigger weatherDataSyncTrigger(){
SimpleScheduleBuilder schedBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1800).repeatForever();
return TriggerBuilder.newTrigger().forJob(weatherDataSyncJobDetail())
.withDescription("WeatherDataSyncTrigger").withSchedule(schedBuilder).build();
}
}

public class WeatherDataSyncJob extends QuartzJobBean {
private final static Logger logger = LoggerFactory.getLogger(WeatherDataSyncJob.class);
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
logger.info("Weather Data Sync Job");
}
}
获取城市数据

http://mobile.weather.com.cn/js/citylist.xml

减少调用服务;缓存数据;使用XML存储文件;服务调用本地XML文件;

暂用(citilist.xml)

映射xml数据到java中(使用JASB)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// CityList.java
@XmlRootElement(name = "c")
@XmlAccessorType(XmlAccessType.FIELD) // 通过字段访问
public class CityList {
@XmlElement(name = "d")
private List<City> cityList;
}

// City.java
@XmlRootElement(name = "d")
@XmlAccessorType(XmlAccessType.FIELD) // 通过字段访问
public class City {
@XmlAttribute(name = "d1")
private String cityId;
@XmlAttribute(name = "d2")
private String cityName;
@XmlAttribute(name = "d3")
private String cityCode;
@XmlAttribute(name = "d4")
private String province;
// ...GETTER SETTER
}

// Util
public class XmlBuilder {
// 将XML数据转换成JAVA对应的POJO
public static Object xmlStrToObject(Class<?> clazz, String xmlStr) throws Exception {
Object xmlObject = null;
Reader reader = null;
JAXBContext context = JAXBContext.newInstance(clazz);
// XML转为对象的接口
Unmarshaller unmarshaller = context.createUnmarshaller();
reader = new StringReader(xmlStr);
xmlObject = unmarshaller.unmarshal(reader);
if (reader != null) {
reader.close();
}
return xmlObject;
}
}

// Service
@Service
public class CityDataServiceImpl implements CityDataService {
@Override
public List<City> listCity() throws Exception {
// 读取XML文件
Resource resource = new ClassPathResource("citylist.xml");
BufferedReader br = new BufferedReader(new InputStreamReader(resource.getInputStream(), "utf-8"));
StringBuffer buffer = new StringBuffer();
String line = "";
while ((line = br.readLine()) != null) {
buffer.append(line);
}
br.close();
// XML转化为JAVA对象
CityList cityList = (CityList) XmlBuilder.xmlStrToObject(CityList.class,buffer.toString());
return cityList.getCityList();
}
}

// Modify WeatherService
@Override
public void syncDataByCityId(String cityId) {
String url = WEATHER_URL + "citykey=" + cityId;
this.saveWeatherData(url);
}
private void saveWeatherData(String url) {
String key = url;
String strBody = null;
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
// 缓存不存在,调用接口
ResponseEntity<String> respString = restTemplate.getForEntity(url, String.class);
if (respString.getStatusCodeValue() == 200) {
strBody = respString.getBody();
}
// 数据写入缓存
ops.set(url, strBody, TIME_OUT, TimeUnit.SECONDS);
}

到这一步,基本数据就已经完成了获取和缓存

推荐GUI界面 Redis-GUI

在GUI里面,可以直观的看到所缓存的数据,包括前面提到的City,Weather

接下来就是完成基本的UI界面

到此,整个项目的基本内容就已经实现

接下来,进入到Spring Cloud内容的学习


附录

RestTemplate

RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率;

@2018年8月2日 14:11:16